package com.lancoo.cvas.arrangecourse.coursescheduling.campusscheduling;

import cn.hutool.core.util.StrUtil;
import com.lancoo.cvas.arrangecourse.coursescheduling.allocatetimeslotroom.AllocateNoClashTimeslotRoom;
import com.lancoo.cvas.arrangecourse.coursescheduling.allocatetimeslotroom.AllocateTimeslotRoom;
import com.lancoo.cvas.arrangecourse.coursescheduling.assignlist.NoClashAssign;
import com.lancoo.cvas.arrangecourse.coursescheduling.assignlist.NormalAssign;
import com.lancoo.cvas.arrangecourse.dataprocess.GenerateSchedulingData;
import com.lancoo.cvas.arrangecourse.entities.ClassHour;
import com.lancoo.cvas.arrangecourse.entities.TeachingClassUnit;
import com.lancoo.cvas.arrangecourse.entities.TimeslotRoom;
import com.lancoo.cvas.arrangecourse.rulecheck.RuleInit;
import com.lancoo.cvas.pojo.common.FixType;
import com.lancoo.cvas.pojo.dto.ArrangeCourseRuleDto;
import com.lancoo.cvas.pojo.dto.ArrangeTeacherRuleDto;
import com.lancoo.cvas.pojo.dto.ArrangeTeachingClassDto;
import com.lancoo.cvas.pojo.dto.ArrangeTeachingClassRuleDto;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @Description 指定排课/教师尽量排的课程
 * @Author Fortysun
 * @Date 2023-03-28 9:25
 */
@Slf4j
public class FixScheduling extends GenerateSchedulingData {

    //声明排课的对象
    protected NormalAssign normalAssign;

    /**
     * 构造函数，尽量排规则处理类，同时传递跨校区排课标识
     *
     * @param ruleInit 排课规则初始化对象
     */
    public FixScheduling(RuleInit ruleInit) {
        //初始化排课的对象
        if (normalAssign == null) {
            //初始化排课使用的教室课时分配的类，分配无冲突的教室课时分组信息；
            AllocateTimeslotRoom allocateTimeslotRoom = new AllocateNoClashTimeslotRoom();
            //初始化拆班排课，需要满足的规则
            normalAssign = new NoClashAssign(ruleInit.getFixCourseRule(), allocateTimeslotRoom);
        }
    }

    /**
     * 处理尽量排排课，一定排课的内容
     *
     * @param taskId                排课计划id
     * @param teachingClassList     教学班列表
     * @param teacherRuleList       教师排课规则列表
     * @param courseRuleList        课程排课规则信息列表
     * @param timeslotRoomList      教室课时分组列表
     * @param classHourMap          锁定的课表信息
     * @param teachingClassRuleList 固排规则
     * @return
     */
    public LinkedList<TeachingClassUnit> start(Integer taskId,
                                               List<ArrangeTeachingClassDto> teachingClassList,
                                               List<ArrangeTeacherRuleDto> teacherRuleList,
                                               List<ArrangeCourseRuleDto> courseRuleList,
                                               LinkedList<TimeslotRoom> timeslotRoomList,
                                               ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap,
                                               List<ArrangeTeachingClassRuleDto> teachingClassRuleList,
                                               boolean withRoom) {

        if ((teachingClassRuleList != null && !teachingClassRuleList.isEmpty())) {
            //将尽量排的教学班，做好标记, 并从教学班列表中，筛选出尽量排的教学班
            List<ArrangeTeachingClassDto> fixTeachingClassList = markFixTimeslot(teachingClassList, teacherRuleList, courseRuleList, teachingClassRuleList,withRoom);
            //课程规则
            teachingClassRuleList = teachingClassRuleList.stream()
                    .filter(o -> 1 == o.getFlag()
                            && o.getType() == FixType.Course.getCode()
                    )
                    .collect(Collectors.toList());
            //开始优先排排课
            return startFixCourse(taskId, fixTeachingClassList, timeslotRoomList, classHourMap,teachingClassRuleList);
        }

        log.info("FixScheduling--start:未设置教师和课程优先排规则，无需优先排排课~！");
        return null;
    }

    /**
     * 开始优先排排课
     *
     * @param taskId            排课计划id
     * @param teachingClassList 进行排课的教学班列表
     * @param timeslotRoomList  用来分配的教室课时分组信息
     * @param classHourMap      生成的课表map对象
     * @return
     */
    protected LinkedList<TeachingClassUnit> startFixCourse(Integer taskId,
                                                           List<ArrangeTeachingClassDto> teachingClassList,
                                                           LinkedList<TimeslotRoom> timeslotRoomList,
                                                           ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap,
                                                           List<ArrangeTeachingClassRuleDto> courseFixList) {
        if (teachingClassList != null && !teachingClassList.isEmpty()) {
            log.info("FixScheduling--start:开始进行优先排排课，需要进行优先排的教学班数量为：" + teachingClassList.size());
            //进行优先排排课
            LinkedList<TeachingClassUnit> teachingClassUnitList = normalAssign.start(taskId,
                    teachingClassList, null, timeslotRoomList, classHourMap,courseFixList);

            log.info("#FixScheduling.start#--优先排排课完毕，剩余未分配教学班排课单元数量" +
                    (teachingClassUnitList != null ? teachingClassUnitList.size() : 0));

            return teachingClassUnitList;

        }

        return null;
    }

    /**
     * 标记尽量排的课程信息
     *
     * @param teachingClassList     教学班列表
     * @param teacherRuleList       教师排课规则列表
     * @param courseRuleList        课程排课规则列表
     * @param teachingClassRuleList 固排规则
     * @return
     */
    private List<ArrangeTeachingClassDto> markFixTimeslot(List<ArrangeTeachingClassDto> teachingClassList,
                                                          List<ArrangeTeacherRuleDto> teacherRuleList,
                                                          List<ArrangeCourseRuleDto> courseRuleList,
                                                          List<ArrangeTeachingClassRuleDto> teachingClassRuleList,
                                                          boolean withRoom) {
        //用于保存教师优先排排课的教学班信息列表；
        List<ArrangeTeachingClassDto> fixTeachingClassList = null;
        if (teachingClassList != null && !teachingClassList.isEmpty()) {
//            if (teacherRuleList != null && !teacherRuleList.isEmpty()) {
//                log.info("FixScheduling--markFixTimeslot:获取教师优先排规则{}条", teacherRuleList.size());
//                //将教师优先排设置的课时信息，添加到教学班列表
//                teachingClassList = markTeacherFixTimeslot(teachingClassList, teacherRuleList);
//            }
//
//            if (courseRuleList != null && !courseRuleList.isEmpty()) {
//                log.info("FixScheduling--markFixTimeslot:获取课程优先排规则{}条", courseRuleList.size());
//                //将教师优先排设置的课时信息，添加到教学班列表
//                teachingClassList = markCourseFixTimeslot(teachingClassList, courseRuleList);
//            }

            if (teachingClassRuleList != null && !teachingClassRuleList.isEmpty()) {
                //筛选固排信息
                teachingClassRuleList = teachingClassRuleList.stream()
                        .filter(o -> 1 == o.getFlag())
                        .collect(Collectors.toList());
                log.info("FixScheduling--markFixTimeslot:获取教学班固排规则{}条", teachingClassRuleList.size());
                //将固排排设置的课时信息，添加到教学班列表
                teachingClassList = markFixTimeslot(teachingClassList, teachingClassRuleList);
            }

            //筛选出标记了优先排的教学班
            fixTeachingClassList = teachingClassList.stream()
                    .filter(teachingClass -> teachingClass.getFixTimeslotCodes() != null
                            && !teachingClass.getFixTimeslotCodes().isEmpty()
                    && (!withRoom || StrUtil.isNotEmpty(teachingClass.getRoomId()))
                    )//筛选已经标记的教学班
                    .collect(Collectors.toList());
        }

        if (fixTeachingClassList == null
                || fixTeachingClassList.isEmpty()) {
            log.info("FixScheduling--markFixTimeslot:未设置固排规则");
        } else {
            //将优先排的教学班，从总的教学班中排除掉
            teachingClassList.removeAll(fixTeachingClassList);
        }

        return fixTeachingClassList;
    }


    /**
     * 将固排设置的课时信息，添加到教学班列表
     *
     * @param teachingClassList     教学班列表
     * @param teachingClassRuleList 固排规则
     * @return
     */
    private List<ArrangeTeachingClassDto> markFixTimeslot(List<ArrangeTeachingClassDto> teachingClassList,
                                                          List<ArrangeTeachingClassRuleDto> teachingClassRuleList) {
        if (teachingClassList != null && !teachingClassList.isEmpty()
                && teachingClassRuleList != null && !teachingClassRuleList.isEmpty()) {
            log.info("FixScheduling--markFixTimeslot:获取固排规则{}条", teachingClassRuleList.size());
            for (ArrangeTeachingClassRuleDto fixRule : teachingClassRuleList) {
                //定义列表遍历的迭代器
                Iterator iterator = teachingClassList.iterator();
                while (iterator.hasNext()) {
                    ArrangeTeachingClassDto teachingClass = (ArrangeTeachingClassDto) iterator.next();
                    //判断教学班教师中，是否包含该教师的固定排信息
                    if (teachingClass.getTeachingClassId().equals(fixRule.getTeachingClassId())
                        && teachingClass.getHourType().equals(fixRule.getHourType())) {

                        //获取课时编码，0102
                        String timeslotCode = fixRule.getTimeCode();
                        //更新教学班信息中的，尽量排字符串属列表性
                        teachingClass.setFixTimeslotCodes(updateFixTimeslotCodes(teachingClass.getFixTimeslotCodes(),
                                timeslotCode));
                    }
                }
            }
        }

        return teachingClassList;
    }

    /**
     * 将教师优先排设置的课时信息，添加到教学班列表
     *
     * @param teachingClassList 教学班列表
     * @param teacherRuleList   教师设置的优先排列表
     * @return
     */
    private List<ArrangeTeachingClassDto> markTeacherFixTimeslot(List<ArrangeTeachingClassDto> teachingClassList,
                                                                 List<ArrangeTeacherRuleDto> teacherRuleList) {
        if (teachingClassList != null && !teachingClassList.isEmpty()
                && teacherRuleList != null && !teacherRuleList.isEmpty()) {
            log.info("FixScheduling--markTeacherFixTimeslot:获取教师优先排规则{}条", teacherRuleList.size());
            for (ArrangeTeacherRuleDto teacherRule : teacherRuleList) {
                //定义列表遍历的迭代器
                Iterator iterator = teachingClassList.iterator();
                while (iterator.hasNext()) {
                    ArrangeTeachingClassDto teachingClass = (ArrangeTeachingClassDto) iterator.next();
                    //判断教学班教师中，是否包含该教师的固定排信息
                    if (isTeacherIdExist(teachingClass.getTeacherIds(), teacherRule.getTeacherId() + "")) {

                        //获取课时编码，0102
                        String timeslotCode = teacherRule.getTimeCode();
                        //更新教学班信息中的，尽量排字符串属列表性
                        teachingClass.setFixTimeslotCodes(updateFixTimeslotCodes(teachingClass.getFixTimeslotCodes(),
                                timeslotCode));
                    }
                }
            }
        }

        return teachingClassList;
    }

    /**
     * 将课程优先排设置的课时信息，标记到教学班列表
     *
     * @param teachingClassList 教学班列表
     * @param courseRuleList    课程优先排规则列表
     * @return
     */
    private List<ArrangeTeachingClassDto> markCourseFixTimeslot(List<ArrangeTeachingClassDto> teachingClassList,
                                                                List<ArrangeCourseRuleDto> courseRuleList) {
        //将课程优先排的课时，标记到教学班
        if (teachingClassList != null && !teachingClassList.isEmpty()
                && courseRuleList != null && !courseRuleList.isEmpty()) {
            log.info("FixScheduling--markCourseFixTimeslot:获取课程优先排规则{}条", courseRuleList.size());
            //定义遍历课程优先排列表
            for (ArrangeCourseRuleDto courseRule : courseRuleList) {
                for (ArrangeTeachingClassDto teachingClass : teachingClassList) {
                    //通过教学班id，进行匹配
                    if (courseRule.getTeachingClassId().equals(teachingClass.getTeachingClassId())) {
                        //将数据添加到优先排的教学班列表
                        //获取课时编码，0102
                        String timeslotCode = courseRule.getTimeCode();
                        //更新教学班信息中的，尽量排字符串属列表性
                        teachingClass.setFixTimeslotCodes(updateFixTimeslotCodes(teachingClass.getFixTimeslotCodes(),
                                timeslotCode));
                    }
                }
            }
        }
        return teachingClassList;
    }

    /**
     * 更新教师尽量排规则列表信息
     *
     * @param fixTimeslotCodes 原来的教师尽量排规则列表
     * @param timeslotCode     添加到优先排规则的课时编码信息
     * @return
     */
    private List<String> updateFixTimeslotCodes(List<String> fixTimeslotCodes,
                                                String timeslotCode) {

        if (StrUtil.isNotEmpty(timeslotCode)){
            if (fixTimeslotCodes == null) {
                //创建列表
                fixTimeslotCodes = new ArrayList<>();
            }
            //如果不存在重复的，则将其加入列表
            if (!fixTimeslotCodes.contains(timeslotCode)) {
                //将这条规则加入列表
                fixTimeslotCodes.add(timeslotCode);
            }
        }

        return fixTimeslotCodes;
    }
}
